package etxt2db.minorthird;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import edu.cmu.minorthird.classify.ClassLabel;
import edu.cmu.minorthird.classify.Example;
import edu.cmu.minorthird.classify.ExampleSchema;
import edu.cmu.minorthird.classify.sequential.SequenceDataset;
import edu.cmu.minorthird.text.Annotator;
import edu.cmu.minorthird.text.Span;
import edu.cmu.minorthird.text.TextLabels;
import edu.cmu.minorthird.text.Token;
import edu.cmu.minorthird.text.learn.SpanFeatureExtractor;
import edu.cmu.minorthird.ui.Recommended;
import edu.cmu.minorthird.util.ProgressCounter;


public abstract class MultiClassAbstractBatchAnnotatorLearner extends
		MultiClassAnnotatorLearner {
	private static Logger log = Logger.getLogger(MultiClassAbstractBatchAnnotatorLearner.class);

	protected SpanFeatureExtractor fe;
	protected List<String> annotationTypes = new ArrayList<String>();
	protected SequenceDataset seqData;
	protected MultiClassExtraction2TaggingReduction reduction;

	public MultiClassAbstractBatchAnnotatorLearner() {
            this(new Recommended.TokenFE(),new MultiClassInsideOutsideReduction());
	}
	public MultiClassAbstractBatchAnnotatorLearner(SpanFeatureExtractor fe,MultiClassExtraction2TaggingReduction reduction) {
            this.reduction = reduction;
            this.fe = fe;
            seqData = new SequenceDataset();
	}

    public void reset() { 
        seqData = new SequenceDataset(); 
    }


  /** Scheme for reducing extraction to a token-classification problem */
	public MultiClassExtraction2TaggingReduction getTaggingReduction() { return reduction; }
	public void setTaggingReduction(MultiClassExtraction2TaggingReduction reduction) { this.reduction = reduction; }

    public String getTaggingReductionHelp() { return "Scheme for reducing extraction to a token-classification problem"; }

  /** Feature extractor used for tokens */
	public SpanFeatureExtractor getSpanFeatureExtractor()	{	return fe; }
	public void setSpanFeatureExtractor(SpanFeatureExtractor fe) {this.fe = fe;	}

	/** The spanType of the annotation produced by the learned annotator. */
	public void setAnnotationType(List<String> type) { 
		annotationTypes=type; 
	}
	
	public List<String> getAnnotationType(String type) {
		return annotationTypes;
	}

  //
  // buffer data
  //

	// temporary storage
	private Iterator<Span> documentLooper;

	/** Accept the pool of unlabeled documents. */
	public void setDocumentPool(Iterator<Span> documentLooper) { this.documentLooper = documentLooper; }

	/** Ask for labels on every document. */
	public boolean hasNextQuery() {	return documentLooper.hasNext();}

	/** Return the next unlabeled document. */
	public Span nextQuery() {	return documentLooper.next();	}

	/** Accept the answer to the last query. */
	public void setAnswer(MultiClassAnnotationExample answeredQuery)
	{
		reduction.reduceExtraction2Tagging(answeredQuery);
		TextLabels answerLabels = reduction.getTaggedLabels();
		
		Span document = answeredQuery.getDocumentSpan();
		Example[] sequence = new Example[document.size()];
		for(String tokenProp : reduction.getTokenProp()){
			for (int i=0; i<document.size(); i++) {
				Token tok = document.getToken(i);
				String value = answerLabels.getProperty(tok,tokenProp);
				if (value!=null) {
					ClassLabel classLabel = new ClassLabel(value);
					Span tokenSpan = document.subSpan(i,1);
					Example example = new Example(fe.extractInstance(answeredQuery.getLabels(),tokenSpan), classLabel);
					if(!value.equals(ExampleSchema.NEG_CLASS_NAME) || sequence[i]==null){
						sequence[i] = example;
					}
				} else {
					log.warn("ignoring "+document.getDocumentId()+" because token "+i+" not labeled in "+document);
					return;
				}
			}
		}
		seqData.addSequence( sequence );
	}

	/** Return the learned annotator.
	 */
	abstract public Annotator getAnnotator();

	/** Get the constructed sequence data.
	 */
	public SequenceDataset getSequenceDataset()
	{
		return seqData;
	}
}
